/* * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.devmarvel.creditcardentrydemo;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.ComponentParent;
import ohos.agp.components.TextField;
import ohos.agp.utils.Rect;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;

import java.util.ArrayList;
import java.util.HashMap;

/**
 * Description:
 * Create by dance, at 2018/12/17
 */
public final class KeyboardUtils {

    public static int sDecorViewInvisibleHeightPre;
    private static final HashMap<Component, OnSoftInputChangedListener> listenerMap = new HashMap<>();

    private KeyboardUtils() {}

    /**
     * Register soft input changed listener.
     *
     * @param listener The soft input changed listener.
     */
    public static void registerSoftInputChangedListener(final Component component, final OnSoftInputChangedListener listener) {
        if (component != null) {
            sDecorViewInvisibleHeightPre = getSoftInputHeight(component);
            listenerMap.put(component, listener);
            ComponentContainer decorView = getDecorView(component);

            // 设置了页面根容器，则用页面根容器监听页面高度变化
            decorView.setLayoutRefreshedListener(new Component.LayoutRefreshedListener() {
                @Override
                public void onRefreshed(Component component) {
                    component.getContext().getUITaskDispatcher().delayDispatch(new Runnable() {
                        @Override
                        public void run() {
                            int height = getSoftInputHeight(component);
                            if (sDecorViewInvisibleHeightPre != height) {
                                // 通知所有弹窗的监听器输入法高度变化了
                                for (OnSoftInputChangedListener changedListener : listenerMap.values()) {
                                    changedListener.onSoftInputChanged(height);
                                }
                                sDecorViewInvisibleHeightPre = height;
                            }
                        }
                    }, 10);
                }
            });
        } else {
            throw new IllegalArgumentException("注册软键盘高度变化监听必须传入页面中的任意一个控件，不可为null");
        }
    }

    public static void removeLayoutChangeListener(Component component) {
        listenerMap.remove(component);
    }

    /**
     * 获取软键盘的高度
     *
     * @param component 任意一个页面中的控件
     * @return 高度，单位px
     */
    public static int getSoftInputHeight(Component component) {
        Rect rect = new Rect();
        component.getWindowVisibleRect(rect);
        return rect.bottom == 0 ? 0 : getScreenHeight(component.getContext()) - rect.bottom;
    }

    /**
     * 获取根容器
     *
     * @param component 根容器中的任意一个控件
     * @return 根容器
     */
    private static ComponentContainer getDecorView(Component component) {
        if (component != null) {
            ComponentParent componentParent = component.getComponentParent();
            if (componentParent == null) {
                return (ComponentContainer) component;
            } else {
                return getDecorView((Component) componentParent);
            }
        } else {
            return null;
        }
    }

    /**
     * 获取屏幕宽度
     *
     * @param context 上下文
     * @return 屏幕宽度，单位px
     */
    public static int getScreenWdith(Context context) {
        return context.getResourceManager().getDeviceCapability().width * context.getResourceManager().getDeviceCapability().screenDensity / 160;
    }

    /**
     * 获取屏幕的高度，包含状态栏，导航栏
     *
     * @param context 上下文
     * @return 高度，单位px
     */
    public static int getScreenHeight(Context context) {
        Display display = DisplayManager.getInstance().getDefaultDisplay(context).get();
        return display.getRealAttributes().height;
    }

    /**
     * 获取应用界面可见高度，不包含导航和状态栏
     *
     * @param context 上下文
     * @return 高度，单位px
     */
    public static int getHapHeight(Context context) {
        Display display = DisplayManager.getInstance().getDefaultDisplay(context).get();
        return display.getAttributes().height;
    }

    /**
     * 获取状态栏高度
     *
     * @param component 任意一个处于布局中的控件
     * @return 状态栏高度，单位px
     */
    public static int getStatusBarHeight(Component component) {
        int[] locationOnScreen = getDecorView(component).getLocationOnScreen();
        return locationOnScreen[1];
    }

    /**
     * 获取导航栏高度
     *
     * @param component 任意一个处于布局中的控件
     * @return 导航栏高度，单位px
     */
    public static int getNavBarHeight(Component component) {
        int screenHeight = getScreenHeight(component.getContext());
        int appHeight = getHapHeight(component.getContext());
        int statusBarHeight = getStatusBarHeight(component);
        return screenHeight - appHeight - statusBarHeight;
    }

    /**
     * 控制输入法的显示
     *
     * @param component TextField控件
     */
    public static void showSoftInput(Component component) {
        if (component instanceof TextField) {
            component.requestFocus();
            component.simulateClick();
        }
    }

    /**
     * 隐藏输入法
     *
     * @param component 布局中的任意一个控件
     */
    public static void hideSoftInput(Component component) {
        if (component != null) {
            ArrayList<TextField> textFields = new ArrayList<>();
            findAllEditText(textFields, getDecorView(component));
            for (int i = 0; i < textFields.size(); i++) {
                textFields.get(i).clearFocus();
            }
        }
    }

    public static void findAllEditText(ArrayList<TextField> list, ComponentContainer container) {
        for (int i = 0; i < container.getChildCount(); i++) {
            Component component = container.getComponentAt(i);
            if (component instanceof TextField) {
                list.add((TextField) component);
            } else if (component instanceof ComponentContainer) {
                findAllEditText(list, (ComponentContainer) component);
            }
        }
    }

    public static void moveDown(ComponentContainer container) {
        float translationY = container.getTranslationY();
        AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setDuration(100);
        animatorValue.setCurveType(Animator.CurveType.OVERSHOOT);
        animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                container.setTranslationY((1 - v) * translationY);
            }
        });
        animatorValue.start();
    }

    private static int correctKeyboardHeight = 0;

    public static void moveUpToKeyboard(final int keyboardHeight, final ComponentContainer pv) {
        correctKeyboardHeight = keyboardHeight;
        moveUpToKeyboardInternal(correctKeyboardHeight, pv);
    }

    private static void moveUpToKeyboardInternal(int keyboardHeight, ComponentContainer container) {
        if (container == null) {
            return;
        }
        // 判断是否盖住输入框
        ArrayList<TextField> allEts = new ArrayList<>();
        findAllEditText(allEts, container);
        TextField focusEt = null;
        for (TextField et : allEts) {
            if (et.isFocused()) {
                focusEt = et;
                break;
            }
        }

        int screenHeight = getScreenHeight(container.getContext());
        int focusEtTop;
        int focusBottom = 0;
        if (focusEt != null) {
            int[] locations = focusEt.getLocationOnScreen();
            focusEtTop = locations[1];
            focusBottom = focusEtTop + focusEt.getEstimatedHeight();
        }

        // 如果只要输入框没被盖住，就不移动
        if (focusBottom + keyboardHeight < screenHeight) {
            return;
        }
        int overflowHeight = focusBottom + keyboardHeight - screenHeight;

        // overflowHeight=0说明没有触发移动
        if (overflowHeight == 0) {
            return;
        }

        AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setDuration(200);
        animatorValue.setCurveType(Animator.CurveType.OVERSHOOT);
        animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                container.setTranslationY(-overflowHeight * v);
            }
        });
        animatorValue.start();
    }

    /**
     * 获取应用界面可见高度，不包含导航和状态栏
     *
     * @param context 上下文
     * @return 高度，单位px
     */
    public static int getAppHeight(Context context) {
        Display display = DisplayManager.getInstance().getDefaultDisplay(context).get();
        return display.getAttributes().height;
    }

    public interface OnSoftInputChangedListener {
        void onSoftInputChanged(int height);
    }

}